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