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