TrinityCore
ThreatManager.h
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #ifndef TRINITY_THREATMANAGER_H
19 #define TRINITY_THREATMANAGER_H
20
21#include "Common.h"
22#include "IteratorPair.h"
23#include "ObjectGuid.h"
24#include "SharedDefines.h"
25#include <array>
26#include <memory>
27#include <unordered_map>
28#include <vector>
29
30class Creature;
31class Unit;
32class SpellInfo;
33
34/********************************************************************************************************************************************************\
35 * DEV DOCUMENTATION: THREAT SYSTEM *
36 * (future devs: please keep this up-to-date if you change the system) *
37 * The threat system works based on dynamically allocated threat list entries. *
38 * *
39 * Each such entry is a ThreatReference object, which is always stored in exactly three places: *
40 * - The threatened unit's (from now: reference "owner") sorted and unsorted threat lists *
41 * - The threatening unit's (from now: reference "victim") threatened-by-me list *
42 * A ThreatReference object carries the following implicit guarantees: *
43 * - Both owner and victim are valid units, which are currently in the world. Neither can be nullptr. *
44 * - There is an active combat reference between owner and victim. *
45 * *
46 * Note that (threat => combat) is a strong guarantee provided in conjunction with CombatManager. Thus: *
47 * - Adding threat will also create a combat reference between the units if one doesn't exist yet (even if the owner can't have a threat list!) *
48 * - Ending combat between two units will also delete any threat references that may exist between them. *
49 * *
50 * To manage a creature's threat list, ThreatManager maintains a heap of threat reference const pointers. *
51 * This heap is kept well-structured in all methods that modify ThreatReference, and is used to select the next target. *
52 * *
53 * Selection uses the following properties on ThreatReference, in order: *
54 * - Online state (one of ONLINE, SUPPRESSED, OFFLINE): *
55 * - ONLINE: Normal threat state, target is valid and attackable *
56 * - SUPPRESSED: Target is attackable, but inopportune. This is used for targets under immunity effects and damage-breaking CC. *
57 * Targets with SUPPRESSED threat can still be valid targets, but any target with ONLINE threat will be preferred. *
58 * - OFFLINE: The target is, for whatever reason, not valid at this time (for example, IMMUNE_TO_X flags or game master state). *
59 * These targets can never be selected, and GetCurrentVictim will return nullptr if all targets are OFFLINE (typically causing evade). *
60 * - Related methods: GetOnlineState, IsOnline, IsAvailable, IsOffline *
61 * - Taunt state (one of TAUNT, NONE, DETAUNT), the names speak for themselves *
62 * - Related methods: GetTauntState, IsTaunting, IsDetaunted *
63 * - Actual threat value (GetThreat) *
64 * *
65 * The current (= last selected) victim can be accessed using GetCurrentVictim. *
66 * Beyond that, ThreatManager has a variety of helpers and notifiers, which are documented inline below. *
67 * *
68 * 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 *
69 * that doesn't mean you're scot free. A variety of actions (casting spells, teleporting units, and so forth) can cause changes to *
70 * the threat list. Use with care - or default to GetModifiableThreatList(), which inherently copies entries. *
71\********************************************************************************************************************************************************/
72
73class ThreatReference;
75{
77 bool operator()(ThreatReference const* a, ThreatReference const* b) const;
78};
79
80// Please check Game/Combat/ThreatManager.h for documentation on how this class works!
82{
83 public:
84 class Heap;
86 static const uint32 THREAT_UPDATE_INTERVAL = 1000u;
87
88 static bool CanHaveThreatList(Unit const* who);
89
90 ThreatManager(Unit* owner);
92 // called from ::Create methods just after construction (once all fields on owner have been populated)
93 // should not be called from anywhere else
94 void Initialize();
95 // called from Creature::Update (only creatures can have their own threat list)
96 // should not be called from anywhere else
97 void Update(uint32 tdiff);
98
99 // never nullptr
100 Unit* GetOwner() const { return _owner; }
101 // can our owner have a threat list?
102 // identical to ThreatManager::CanHaveThreatList(GetOwner())
103 bool CanHaveThreatList() const { return _ownerCanHaveThreatList; }
104 // returns the current victim - this can be nullptr if owner's threat list is empty, or has only offline targets
105 Unit* GetCurrentVictim();
106 Unit* GetLastVictim() const;
107 // returns an arbitrary non-offline victim from owner's threat list if one exists, nullptr otherwise
108 Unit* GetAnyTarget() const;
109
110 // are there any entries in owner's threat list?
111 bool IsThreatListEmpty(bool includeOffline = false) const;
112 // is there a threat list entry on owner's threat list with victim == who?
113 bool IsThreatenedBy(ObjectGuid const& who, bool includeOffline = false) const;
114 // is there a threat list entry on owner's threat list with victim == who?
115 bool IsThreatenedBy(Unit const* who, bool includeOffline = false) const;
116 // returns ThreatReference amount if a ref exists, 0.0f otherwise
117 float GetThreat(Unit const* who, bool includeOffline = false) const;
118 size_t GetThreatListSize() const;
119 // fastest of the three threat list getters - gets the threat list in "arbitrary" order
120 // iterators will invalidate on adding/removing entries from the threat list; slightly less finicky than GetSorted.
122 // slightly slower than GetUnsorted, but, well...sorted - only use it if you need the sorted property, of course
123 // this iterator pair will invalidate on any modification (even indirect) of the threat list; spell casts and similar can all induce this!
124 // note: current tank is NOT guaranteed to be the first entry in this list - check GetLastVictim separately if you want that!
126 // slowest of the three threat list getters (by far), but lets you modify the threat references - this is also sorted
127 std::vector<ThreatReference*> GetModifiableThreatList();
128
129 // does any unit have a threat list entry with victim == this.owner?
130 bool IsThreateningAnyone(bool includeOffline = false) const;
131 // is there a threat list entry on who's threat list for this.owner?
132 bool IsThreateningTo(ObjectGuid const& who, bool includeOffline = false) const;
133 // is there a threat list entry on who's threat list for this.owner?
134 bool IsThreateningTo(Unit const* who, bool includeOffline = false) const;
135 auto const& GetThreatenedByMeList() const { return _threatenedByMe; }
136
137 // Notify the ThreatManager that its owner may now be suppressed on others' threat lists (immunity or damage-breakable CC being applied)
138 void EvaluateSuppressed(bool canExpire = false);
140 void AddThreat(Unit* target, float amount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false, bool ignoreRedirects = false);
141 void ScaleThreat(Unit* target, float factor);
142 // Modify target's threat by +percent%
143 void ModifyThreatByPercent(Unit* target, int32 percent) { if (percent) ScaleThreat(target, 0.01f*float(100 + percent)); }
144 // Resets the specified unit's threat to zero
145 void ResetThreat(Unit* target) { ScaleThreat(target, 0.0f); }
146 // Sets the specified unit's threat to be equal to the highest entry on the threat list
147 void MatchUnitThreatToHighestThreat(Unit* target);
148 // Notify the ThreatManager that we have a new taunt aura (or a taunt aura expired)
149 void TauntUpdate();
150 // Sets all threat refs in owner's threat list to have zero threat
151 void ResetAllThreat();
152 // Removes specified target from the threat list
153 void ClearThreat(Unit* target);
154 void ClearThreat(ThreatReference* ref);
155 // Removes all targets from the threat list (will cause evade in UpdateVictim if called)
156 void ClearAllThreat();
157
158 // Fixate on the passed target; this target will always be selected until the fixate is cleared
159 // (if the target is not in the threat list, does nothing)
160 void FixateTarget(Unit* target);
161 void ClearFixate() { FixateTarget(nullptr); }
162 Unit* GetFixateTarget() const;
163
165 // what it says on the tin - call AddThreat on everything that's threatened by us with the specified params
166 void ForwardThreatForAssistingMe(Unit* assistant, float baseAmount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false);
167 // delete all ThreatReferences with victim == owner
168 void RemoveMeFromThreatLists(bool (*unitFilter)(Unit const* otherUnit));
169 // re-calculates the temporary threat modifier from auras on myself
170 void UpdateMyTempModifiers();
171 // re-calculate SPELL_AURA_MOD_THREAT modifiers
172 void UpdateMySpellSchoolModifiers();
173
175 // Register a redirection effect that redirects pct% of threat generated by owner to victim
176 void RegisterRedirectThreat(uint32 spellId, ObjectGuid const& victim, uint32 pct);
177 // Unregister a redirection effort for all victims
178 void UnregisterRedirectThreat(uint32 spellId);
179 // Unregister a redirection effect for a specific victim
180 void UnregisterRedirectThreat(uint32 spellId, ObjectGuid const& victim);
181
182 private:
183 Unit* const _owner;
185
187 static bool CompareReferencesLT(ThreatReference const* a, ThreatReference const* b, float aWeight);
188 static float CalculateModifiedThreat(float threat, Unit const* victim, SpellInfo const* spell);
189
190 // send opcodes (all for my own threat list)
191 void SendClearAllThreatToClients() const;
192 void SendRemoveToClients(Unit const* victim) const;
193 void SendThreatListToClients(bool newHighest) const;
194
196 void PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref);
197 void PurgeThreatListRef(ObjectGuid const& guid);
198
201 std::unique_ptr<Heap> _sortedThreatList;
202 std::unordered_map<ObjectGuid, ThreatReference*> _myThreatListEntries;
203
204 // AI notifies are delayed to ensure we are in a consistent state before we call out to arbitrary logic
205 // threat references might register themselves here when ::UpdateOffline() is called - MAKE SURE THIS IS PROCESSED JUST BEFORE YOU EXIT THREATMANAGER LOGIC
206 void ProcessAIUpdates();
207 void RegisterForAIUpdate(ObjectGuid const& guid) { _needsAIUpdate.push_back(guid); }
208 std::vector<ObjectGuid> _needsAIUpdate;
209
210 // picks a new victim - called from ::Update periodically
211 void UpdateVictim();
212 ThreatReference const* ReselectVictim();
215
217 void PutThreatenedByMeRef(ObjectGuid const& guid, ThreatReference* ref);
218 void PurgeThreatenedByMeRef(ObjectGuid const& guid);
219 std::unordered_map<ObjectGuid, ThreatReference*> _threatenedByMe; // these refs are entries for myself on other units' threat lists
220 std::array<float, MAX_SPELL_SCHOOL> _singleSchoolModifiers; // most spells are single school - we pre-calculate these and store them
221 mutable std::unordered_map<std::underlying_type<SpellSchoolMask>::type, float> _multiSchoolModifiers; // these are calculated on demand
222
223 // 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)
224 void UpdateRedirectInfo();
225 std::vector<std::pair<ObjectGuid, uint32>> _redirectInfo; // current redirection targets and percentages (updated from registry in ThreatManager::UpdateRedirectInfo)
226 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)
227
228 public:
229 ThreatManager(ThreatManager const&) = delete;
231
233 {
234 private:
235 std::function<ThreatReference const* ()> _generator;
237
239 explicit ThreatListIterator(std::function<ThreatReference const* ()>&& generator)
240 : _generator(std::move(generator)), _current(_generator()) {}
241
242 public:
243 ThreatReference const* operator*() const { return _current; }
244 ThreatReference const* operator->() const { return _current; }
245 ThreatListIterator& operator++() { _current = _generator(); return *this; }
246 bool operator==(ThreatListIterator const& o) const { return _current == o._current; }
247 bool operator!=(ThreatListIterator const& o) const { return _current != o._current; }
248 bool operator==(std::nullptr_t) const { return _current == nullptr; }
249 bool operator!=(std::nullptr_t) const { return _current != nullptr; }
250 };
251
252 friend class ThreatReference;
256};
257
258// Please check Game/Combat/ThreatManager.h for documentation on how this class works!
260{
261 public:
262 enum TauntState : uint32 { TAUNT_STATE_DETAUNT = 0, TAUNT_STATE_NONE = 1, TAUNT_STATE_TAUNT = 2 };
263 enum OnlineState { ONLINE_STATE_ONLINE = 2, ONLINE_STATE_SUPPRESSED = 1, ONLINE_STATE_OFFLINE = 0 };
264
265 Creature* GetOwner() const { return _owner; }
266 Unit* GetVictim() const { return _victim; }
267 float GetThreat() const { return std::max<float>(_baseAmount + (float)_tempModifier, 0.0f); }
268 OnlineState GetOnlineState() const { return _online; }
269 bool IsOnline() const { return (_online >= ONLINE_STATE_ONLINE); }
270 bool IsAvailable() const { return (_online > ONLINE_STATE_OFFLINE); }
271 bool IsSuppressed() const { return (_online == ONLINE_STATE_SUPPRESSED); }
272 bool IsOffline() const { return (_online <= ONLINE_STATE_OFFLINE); }
273 TauntState GetTauntState() const { return IsTaunting() ? TAUNT_STATE_TAUNT : _taunted; }
274 bool IsTaunting() const { return _taunted >= TAUNT_STATE_TAUNT; }
275 bool IsDetaunted() const { return _taunted == TAUNT_STATE_DETAUNT; }
276
277 void AddThreat(float amount);
278 void ScaleThreat(float factor);
279 void ModifyThreatByPercent(int32 percent) { if (percent) ScaleThreat(0.01f*float(100 + percent)); }
280 void UpdateOffline();
281
282 void ClearThreat(); // dealloc's this
283
284 protected:
285 static bool FlagsAllowFighting(Unit const* a, Unit const* b);
286
287 explicit ThreatReference(ThreatManager* mgr, Unit* victim) :
288 _owner(reinterpret_cast<Creature*>(mgr->_owner)), _mgr(*mgr), _victim(victim),
289 _baseAmount(0.0f), _tempModifier(0), _taunted(TAUNT_STATE_NONE)
290 {
291 _online = ONLINE_STATE_OFFLINE;
292 }
293
294 virtual ~ThreatReference() = default;
295
296 void UnregisterAndFree();
297
298 bool ShouldBeOffline() const;
299 bool ShouldBeSuppressed() const;
300 void UpdateTauntState(TauntState state = TAUNT_STATE_NONE);
303 void HeapNotifyIncreased();
304 void HeapNotifyDecreased();
305 Unit* const _victim;
308 int32 _tempModifier; // Temporary effects (auras with SPELL_AURA_MOD_TOTAL_THREAT) - set from victim's threatmanager in ThreatManager::UpdateMyTempModifiers
310
311 public:
314
315 friend class ThreatManager;
317};
318
320
321 #endif
#define TC_GAME_API
Definition: Define.h:123
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
ThreatReference const * operator*() const
ThreatListIterator(std::function< ThreatReference const *()> &&generator)
ThreatReference const * operator->() const
ThreatReference const * _current
bool operator==(ThreatListIterator const &o) const
bool operator!=(std::nullptr_t) const
bool operator==(std::nullptr_t) const
bool operator!=(ThreatListIterator const &o) const
ThreatListIterator & operator++()
std::unordered_map< ObjectGuid, ThreatReference * > _threatenedByMe
bool CanHaveThreatList() const
void ClearFixate()
void ModifyThreatByPercent(Unit *target, int32 percent)
uint32 _updateTimer
ThreatManager & operator=(ThreatManager const &)=delete
std::vector< std::pair< ObjectGuid, uint32 > > _redirectInfo
void RegisterForAIUpdate(ObjectGuid const &guid)
void ResetThreat(Unit *target)
std::array< float, MAX_SPELL_SCHOOL > _singleSchoolModifiers
static bool CompareReferencesLT(ThreatReference const *a, ThreatReference const *b, float aWeight)
std::unordered_map< uint32, std::unordered_map< ObjectGuid, uint32 > > _redirectRegistry
auto const & GetThreatenedByMeList() const
static const CompareThreatLessThan CompareThreat
std::unique_ptr< Heap > _sortedThreatList
ThreatReference const * _currentVictimRef
std::unordered_map< ObjectGuid, ThreatReference * > _myThreatListEntries
bool _ownerCanHaveThreatList
std::vector< ObjectGuid > _needsAIUpdate
ThreatManager(ThreatManager const &)=delete
bool _needClientUpdate
Unit *const _owner
ThreatReference const * _fixateRef
std::unordered_map< std::underlying_type< SpellSchoolMask >::type, float > _multiSchoolModifiers
Unit * GetOwner() const
void ModifyThreatByPercent(int32 percent)
bool IsSuppressed() const
bool IsTaunting() const
Creature * GetOwner() const
ThreatManager & _mgr
bool IsDetaunted() const
OnlineState GetOnlineState() const
Creature *const _owner
bool IsOnline() const
Unit *const _victim
TauntState _taunted
bool IsOffline() const
ThreatReference(ThreatReference const &)=delete
float GetThreat() const
TauntState GetTauntState() const
ThreatReference(ThreatManager *mgr, Unit *victim)
OnlineState _online
virtual ~ThreatReference()=default
ThreatReference & operator=(ThreatReference const &)=delete
Unit * GetVictim() const
bool IsAvailable() const
Utility class to enable range for loop syntax for multimap.equal_range uses.
Definition: IteratorPair.h:32
Definition: Unit.h:627
void Update(VignetteData &vignette, WorldObject const *owner)
Definition: Vignette.cpp:90
STL namespace.
bool operator()(ThreatReference const *a, ThreatReference const *b) const