TrinityCore
Loading...
Searching...
No Matches
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 // called from Creature::AtEngage
99 // should not be called from anywhere else
100 void ResetUpdateTimer();
101
102 // never nullptr
103 Unit* GetOwner() const { return _owner; }
104 // can our owner have a threat list?
105 // identical to ThreatManager::CanHaveThreatList(GetOwner())
106 bool CanHaveThreatList() const { return _ownerCanHaveThreatList; }
107 // returns the current victim - this can be nullptr if owner's threat list is empty, or has only offline targets
108 Unit* GetCurrentVictim();
109 Unit* GetLastVictim() const;
110 // returns an arbitrary non-offline victim from owner's threat list if one exists, nullptr otherwise
111 Unit* GetAnyTarget() const;
112
113 // are there any entries in owner's threat list?
114 bool IsThreatListEmpty(bool includeOffline = false) const;
115 // is there a threat list entry on owner's threat list with victim == who?
116 bool IsThreatenedBy(ObjectGuid const& who, bool includeOffline = false) const;
117 // is there a threat list entry on owner's threat list with victim == who?
118 bool IsThreatenedBy(Unit const* who, bool includeOffline = false) const;
119 // returns ThreatReference amount if a ref exists, 0.0f otherwise
120 float GetThreat(Unit const* who, bool includeOffline = false) const;
121 size_t GetThreatListSize() const;
122 // fastest of the three threat list getters - gets the threat list in "arbitrary" order
123 // iterators will invalidate on adding/removing entries from the threat list; slightly less finicky than GetSorted.
125 // slightly slower than GetUnsorted, but, well...sorted - only use it if you need the sorted property, of course
126 // this iterator pair will invalidate on any modification (even indirect) of the threat list; spell casts and similar can all induce this!
127 // note: current tank is NOT guaranteed to be the first entry in this list - check GetLastVictim separately if you want that!
129 // slowest of the three threat list getters (by far), but lets you modify the threat references - this is also sorted
130 std::vector<ThreatReference*> GetModifiableThreatList();
131
132 // does any unit have a threat list entry with victim == this.owner?
133 bool IsThreateningAnyone(bool includeOffline = false) const;
134 // is there a threat list entry on who's threat list for this.owner?
135 bool IsThreateningTo(ObjectGuid const& who, bool includeOffline = false) const;
136 // is there a threat list entry on who's threat list for this.owner?
137 bool IsThreateningTo(Unit const* who, bool includeOffline = false) const;
138 auto const& GetThreatenedByMeList() const { return _threatenedByMe; }
139
140 // Notify the ThreatManager that its owner may now be suppressed on others' threat lists (immunity or damage-breakable CC being applied)
141 void EvaluateSuppressed(bool canExpire = false);
143 void AddThreat(Unit* target, float amount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false, bool ignoreRedirects = false);
144 void ScaleThreat(Unit* target, float factor);
145 // Modify target's threat by +percent%
146 void ModifyThreatByPercent(Unit* target, float percent) { if (percent) ScaleThreat(target, 0.01f * (100.0f + percent)); }
147 // Resets the specified unit's threat to zero
148 void ResetThreat(Unit* target) { ScaleThreat(target, 0.0f); }
149 // Sets the specified unit's threat to be equal to the highest entry on the threat list
150 void MatchUnitThreatToHighestThreat(Unit* target);
151 // Notify the ThreatManager that we have a new taunt aura (or a taunt aura expired)
152 void TauntUpdate();
153 // Sets all threat refs in owner's threat list to have zero threat
154 void ResetAllThreat();
155 // Removes specified target from the threat list
156 void ClearThreat(Unit* target);
157 void ClearThreat(ThreatReference* ref);
158 // Removes all targets from the threat list (will cause evade in UpdateVictim if called)
159 void ClearAllThreat();
160
161 // Fixate on the passed target; this target will always be selected until the fixate is cleared
162 // (if the target is not in the threat list, does nothing)
163 void FixateTarget(Unit* target);
164 void ClearFixate() { FixateTarget(nullptr); }
165 Unit* GetFixateTarget() const;
166
168 // what it says on the tin - call AddThreat on everything that's threatened by us with the specified params
169 void ForwardThreatForAssistingMe(Unit* assistant, float baseAmount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false);
170 // delete all ThreatReferences with victim == owner
171 void RemoveMeFromThreatLists(bool (*unitFilter)(Unit const* otherUnit));
172 // re-calculates the temporary threat modifier from auras on myself
173 void UpdateMyTempModifiers();
174 // re-calculate SPELL_AURA_MOD_THREAT modifiers
175 void UpdateMySpellSchoolModifiers();
176
178 // Register a redirection effect that redirects pct% of threat generated by owner to victim
179 void RegisterRedirectThreat(uint32 spellId, ObjectGuid const& victim, float pct);
180 // Unregister a redirection effort for all victims
181 void UnregisterRedirectThreat(uint32 spellId);
182 // Unregister a redirection effect for a specific victim
183 void UnregisterRedirectThreat(uint32 spellId, ObjectGuid const& victim);
184
185 private:
186 Unit* const _owner;
188
190 static bool CompareReferencesLT(ThreatReference const* a, ThreatReference const* b, float aWeight);
191 static float CalculateModifiedThreat(float threat, Unit const* victim, SpellInfo const* spell);
192
193 // send opcodes (all for my own threat list)
194 void SendClearAllThreatToClients() const;
195 void SendRemoveToClients(Unit const* victim) const;
196 void SendThreatListToClients(bool newHighest) const;
197
199 void PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref);
200 void PurgeThreatListRef(ObjectGuid const& guid);
201
205 std::unique_ptr<Heap> _sortedThreatList;
206 std::unordered_map<ObjectGuid, ThreatReference*> _myThreatListEntries;
207
208 // AI notifies are delayed to ensure we are in a consistent state before we call out to arbitrary logic
209 // threat references might register themselves here when ::UpdateOffline() is called - MAKE SURE THIS IS PROCESSED JUST BEFORE YOU EXIT THREATMANAGER LOGIC
210 void ProcessAIUpdates();
211 void RegisterForAIUpdate(ObjectGuid const& guid);
212 std::vector<ObjectGuid> _needsAIUpdate;
213
214 // picks a new victim - called from ::Update periodically
215 void UpdateVictim();
216 ThreatReference const* ReselectVictim();
219
221 void PutThreatenedByMeRef(ObjectGuid const& guid, ThreatReference* ref);
222 void PurgeThreatenedByMeRef(ObjectGuid const& guid);
223 std::unordered_map<ObjectGuid, ThreatReference*> _threatenedByMe; // these refs are entries for myself on other units' threat lists
224 std::array<float, MAX_SPELL_SCHOOL> _singleSchoolModifiers; // most spells are single school - we pre-calculate these and store them
225 mutable std::unordered_map<std::underlying_type<SpellSchoolMask>::type, float> _multiSchoolModifiers; // these are calculated on demand
226
227 // 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)
228 void UpdateRedirectInfo();
229 std::vector<std::pair<ObjectGuid, float>> _redirectInfo; // current redirection targets and percentages (updated from registry in ThreatManager::UpdateRedirectInfo)
230 std::unordered_map<uint32, std::unordered_map<ObjectGuid, float>> _redirectRegistry; // spellid -> (victim -> pct); all redirection effects on us (removal individually managed by spell scripts because blizzard is dumb)
231
232 public:
233 ThreatManager(ThreatManager const&) = delete;
235
237 {
238 private:
239 std::function<ThreatReference const* ()> _generator;
241
243 explicit ThreatListIterator(std::function<ThreatReference const* ()>&& generator)
244 : _generator(std::move(generator)), _current(_generator()) {}
245
246 public:
247 ThreatReference const* operator*() const { return _current; }
248 ThreatReference const* operator->() const { return _current; }
249 ThreatListIterator& operator++() { _current = _generator(); return *this; }
250 bool operator==(ThreatListIterator const& o) const { return _current == o._current; }
251 bool operator!=(ThreatListIterator const& o) const { return _current != o._current; }
252 bool operator==(std::nullptr_t) const { return _current == nullptr; }
253 bool operator!=(std::nullptr_t) const { return _current != nullptr; }
254 };
255
256 friend class ThreatReference;
260};
261
262// Please check Game/Combat/ThreatManager.h for documentation on how this class works!
264{
265 public:
266 enum TauntState : uint32 { TAUNT_STATE_DETAUNT = 0, TAUNT_STATE_NONE = 1, TAUNT_STATE_TAUNT = 2 };
267 enum OnlineState { ONLINE_STATE_ONLINE = 2, ONLINE_STATE_SUPPRESSED = 1, ONLINE_STATE_OFFLINE = 0 };
268
269 Creature* GetOwner() const { return _owner; }
270 Unit* GetVictim() const { return _victim; }
271 float GetThreat() const { return std::max<float>(_baseAmount + _tempModifier, 0.0f); }
272 OnlineState GetOnlineState() const { return _online; }
273 bool IsOnline() const { return (_online >= ONLINE_STATE_ONLINE); }
274 bool IsAvailable() const { return (_online > ONLINE_STATE_OFFLINE); }
275 bool IsSuppressed() const { return (_online == ONLINE_STATE_SUPPRESSED); }
276 bool IsOffline() const { return (_online <= ONLINE_STATE_OFFLINE); }
277 TauntState GetTauntState() const { return IsTaunting() ? TAUNT_STATE_TAUNT : _taunted; }
278 bool IsTaunting() const { return _taunted >= TAUNT_STATE_TAUNT; }
279 bool IsDetaunted() const { return _taunted == TAUNT_STATE_DETAUNT; }
280
281 void AddThreat(float amount);
282 void ScaleThreat(float factor);
283 void ModifyThreatByPercent(int32 percent) { if (percent) ScaleThreat(0.01f*float(100 + percent)); }
284 void UpdateOffline();
285
286 void ClearThreat(); // dealloc's this
287
288 protected:
289 static bool FlagsAllowFighting(Unit const* a, Unit const* b);
290
291 explicit ThreatReference(ThreatManager* mgr, Unit* victim) :
292 _owner(reinterpret_cast<Creature*>(mgr->_owner)), _mgr(*mgr), _victim(victim),
293 _baseAmount(0.0f), _tempModifier(0.0f), _taunted(TAUNT_STATE_NONE)
294 {
295 _online = ONLINE_STATE_OFFLINE;
296 }
297
298 virtual ~ThreatReference() = default;
299
300 void UnregisterAndFree();
301
302 bool ShouldBeOffline() const;
303 bool ShouldBeSuppressed() const;
304 void UpdateTauntState(TauntState state = TAUNT_STATE_NONE);
307 void HeapNotifyIncreased();
308 void HeapNotifyDecreased();
309 Unit* const _victim;
312 float _tempModifier; // Temporary effects (auras with SPELL_AURA_MOD_TOTAL_THREAT) - set from victim's threatmanager in ThreatManager::UpdateMyTempModifiers
314
315 public:
318
319 friend class ThreatManager;
321};
322
324
325 #endif
#define TC_GAME_API
Definition Define.h:129
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
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
ThreatManager & operator=(ThreatManager const &)=delete
void ResetThreat(Unit *target)
std::array< float, MAX_SPELL_SCHOOL > _singleSchoolModifiers
static bool CompareReferencesLT(ThreatReference const *a, ThreatReference const *b, float aWeight)
auto const & GetThreatenedByMeList() const
static const CompareThreatLessThan CompareThreat
std::unique_ptr< Heap > _sortedThreatList
ThreatReference const * _currentVictimRef
std::unordered_map< ObjectGuid, ThreatReference * > _myThreatListEntries
void ModifyThreatByPercent(Unit *target, float percent)
bool _ownerCanHaveThreatList
std::vector< ObjectGuid > _needsAIUpdate
ThreatManager(ThreatManager const &)=delete
std::unordered_map< uint32, std::unordered_map< ObjectGuid, float > > _redirectRegistry
Unit *const _owner
ThreatReference const * _fixateRef
std::unordered_map< std::underlying_type< SpellSchoolMask >::type, float > _multiSchoolModifiers
Unit * GetOwner() const
std::vector< std::pair< ObjectGuid, float > > _redirectInfo
bool _needThreatClearUpdate
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 Unit.h:635
STL namespace.
bool operator()(ThreatReference const *a, ThreatReference const *b) const